Micron Document
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| SparkN0de-git | SparkN0de |
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Node / ReticulumProjects / MeshChatX.git / files / tests / backend / test_ws_origin_filesync_oracles.py

Displaying Raw • Download


tests/backend/test_ws_origin_filesync_oracles.py HEAD (2bf00cb3) Text, 4.70 KB

T8b949e# SPDX-License-Identifier: 0BSD

T8b949e"""Security oracles for cross-origin WebSocket access and the FileSync sync-root jail.

Invariants under test:

1. A WebSocket upgrade whose Origin is not the local backend origin must be
rejected, regardless of whether HTTP auth is enabled.
2. A cross-origin WebSocket client with no session must not mutate server state.
3. The FileSync sync directory must never resolve to a sensitive identity-tree
directory such as ssl (TLS key material), while ordinary subdirectories
remain selectable.
"""

Tff7b72import T7ee787json
Tff7b72import T7ee787os
Tff7b72from T7ee787unittestT7ee787.T7ee787mock Tff7b72import Te6edf3AsyncMockTb4b4b4, Te6edf3MagicMock

Tff7b72import T7ee787pytest
Tff7b72from T7ee787aiohttp Tff7b72import Te6edf3WSServerHandshakeErrorTb4b4b4, Te6edf3WSMsgType
Tff7b72from T7ee787aiohttpT7ee787.T7ee787test_utils Tff7b72import Te6edf3TestClientTb4b4b4, Te6edf3TestServer

Tff7b72from T7ee787meshchatxT7ee787.T7ee787srcT7ee787.T7ee787backendT7ee787.T7ee787rns_filesync_handler Tff7b72import Te6edf3RnsFilesyncHandler
Tff7b72from T7ee787testsT7ee787.T7ee787backendT7ee787.T7ee787test_http_auth_security Tff7b72import Te6edf3_make_aio_app

Te6edf3EVIL_ORIGIN Tff7b72= Ta5d6ff"Ta5d6ffhttps://evil.exampleTa5d6ff"


Tff7b72def Td2a8ff_patch_ws_broadcastsTb4b4b4(Te6edf3mock_appTb4b4b4)Tb4b4b4:
Te6edf3mock_appTff7b72.Td2a8ffsend_config_to_websocket_clients Tff7b72= Te6edf3AsyncMockTb4b4b4(Tb4b4b4)
Te6edf3mock_appTff7b72.Td2a8ffsend_active_sessions_to_websocket_clients Tff7b72= Te6edf3AsyncMockTb4b4b4(Tb4b4b4)


Tf0883e@pytestTff7b72.Td2a8ffmarkTff7b72.Td2a8ffasyncio
Tf0883e@pytestTff7b72.Td2a8ffmarkTff7b72.Td2a8ffusefixturesTb4b4b4(Ta5d6ff"Ta5d6ffrequire_loopback_tcpTa5d6ff"Tb4b4b4)
Tff7b72async Tff7b72def Td2a8fftest_ws_upgrade_rejects_cross_site_originTb4b4b4(Te6edf3mock_appTb4b4b4)Tb4b4b4:
T8b949e"""Invariant 1: hostile Origin must not complete the /ws upgrade."""
Te6edf3_patch_ws_broadcastsTb4b4b4(Te6edf3mock_appTb4b4b4)
Te6edf3aio_app Tff7b72= Te6edf3_make_aio_appTb4b4b4(Te6edf3mock_appTb4b4b4, Te6edf3use_httpsTff7b72=Tff7b72FalseTb4b4b4)

Tff7b72async Tff7b72with Te6edf3TestClientTb4b4b4(Te6edf3TestServerTb4b4b4(Te6edf3aio_appTb4b4b4)Tb4b4b4) Tff7b72as Te6edf3clientTb4b4b4:
Tff7b72with Te6edf3pytestTff7b72.Td2a8ffraisesTb4b4b4(Te6edf3WSServerHandshakeErrorTb4b4b4)Tb4b4b4:
Tff7b72await Te6edf3clientTff7b72.Td2a8ffws_connectTb4b4b4(Ta5d6ff"Ta5d6ff/wsTa5d6ff"Tb4b4b4, Te6edf3originTff7b72=Te6edf3EVIL_ORIGINTb4b4b4)


Tf0883e@pytestTff7b72.Td2a8ffmarkTff7b72.Td2a8ffasyncio
Tf0883e@pytestTff7b72.Td2a8ffmarkTff7b72.Td2a8ffusefixturesTb4b4b4(Ta5d6ff"Ta5d6ffrequire_loopback_tcpTa5d6ff"Tb4b4b4)
Tff7b72async Tff7b72def Td2a8fftest_telephone_audio_ws_rejects_cross_site_originTb4b4b4(Te6edf3mock_appTb4b4b4)Tb4b4b4:
T8b949e"""Invariant 1 also covers /ws/telephone/audio."""
Te6edf3_patch_ws_broadcastsTb4b4b4(Te6edf3mock_appTb4b4b4)
Te6edf3aio_app Tff7b72= Te6edf3_make_aio_appTb4b4b4(Te6edf3mock_appTb4b4b4, Te6edf3use_httpsTff7b72=Tff7b72FalseTb4b4b4)

Tff7b72async Tff7b72with Te6edf3TestClientTb4b4b4(Te6edf3TestServerTb4b4b4(Te6edf3aio_appTb4b4b4)Tb4b4b4) Tff7b72as Te6edf3clientTb4b4b4:
Tff7b72with Te6edf3pytestTff7b72.Td2a8ffraisesTb4b4b4(Te6edf3WSServerHandshakeErrorTb4b4b4)Tb4b4b4:
Tff7b72await Te6edf3clientTff7b72.Td2a8ffws_connectTb4b4b4(Ta5d6ff"Ta5d6ff/ws/telephone/audioTa5d6ff"Tb4b4b4, Te6edf3originTff7b72=Te6edf3EVIL_ORIGINTb4b4b4)


Tf0883e@pytestTff7b72.Td2a8ffmarkTff7b72.Td2a8ffasyncio
Tf0883e@pytestTff7b72.Td2a8ffmarkTff7b72.Td2a8ffusefixturesTb4b4b4(Ta5d6ff"Ta5d6ffrequire_loopback_tcpTa5d6ff"Tb4b4b4)
Tff7b72async Tff7b72def Td2a8fftest_cross_origin_ws_mutator_does_not_change_stateTb4b4b4(Te6edf3mock_appTb4b4b4)Tb4b4b4:
T8b949e"""Invariant 2: no session and hostile Origin means no state mutation."""
Te6edf3_patch_ws_broadcastsTb4b4b4(Te6edf3mock_appTb4b4b4)
Te6edf3aio_app Tff7b72= Te6edf3_make_aio_appTb4b4b4(Te6edf3mock_appTb4b4b4, Te6edf3use_httpsTff7b72=Tff7b72FalseTb4b4b4)

Tff7b72async Tff7b72with Te6edf3TestClientTb4b4b4(Te6edf3TestServerTb4b4b4(Te6edf3aio_appTb4b4b4)Tb4b4b4) Tff7b72as Te6edf3clientTb4b4b4:
Tff7b72tryTb4b4b4:
Te6edf3ws Tff7b72= Tff7b72await Te6edf3clientTff7b72.Td2a8ffws_connectTb4b4b4(Ta5d6ff"Ta5d6ff/wsTa5d6ff"Tb4b4b4, Te6edf3originTff7b72=Te6edf3EVIL_ORIGINTb4b4b4)
Tff7b72except Tf85149ExceptionTb4b4b4:
Tff7b72return

Te6edf3probe_action Tff7b72= Ta5d6ff"Ta5d6fforacle_cross_origin_probeTa5d6ff"
Tff7b72await Te6edf3wsTff7b72.Td2a8ffsend_strTb4b4b4(
Te6edf3jsonTff7b72.Td2a8ffdumpsTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6fftypeTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffkeyboard_shortcuts.setTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffactionTa5d6ff"Tb4b4b4: Te6edf3probe_actionTb4b4b4,
Ta5d6ff"Ta5d6ffkeysTa5d6ff"Tb4b4b4: Tb4b4b4[Ta5d6ff"Ta5d6ffctrl+alt+9Ta5d6ff"Tb4b4b4]Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4)
Tff7b72tryTb4b4b4:
Tff7b72while Tff7b72TrueTb4b4b4:
Te6edf3msg Tff7b72= Tff7b72await Te6edf3wsTff7b72.Td2a8ffreceiveTb4b4b4(Te6edf3timeoutTff7b72=T79c0ff2Tb4b4b4)
Tff7b72if Te6edf3msgTff7b72.Td2a8fftype Tff7b72in Tb4b4b4(Te6edf3WSMsgTypeTff7b72.Td2a8ffCLOSEDTb4b4b4, Te6edf3WSMsgTypeTff7b72.Td2a8ffERRORTb4b4b4)Tb4b4b4:
Tff7b72break
Tff7b72if Te6edf3msgTff7b72.Td2a8fftype Tff7b72== Te6edf3WSMsgTypeTff7b72.Td2a8ffTEXT Tff7b72and Ta5d6ff"Ta5d6ffkeyboard_shortcutsTa5d6ff" Tff7b72in Te6edf3msgTff7b72.Td2a8ffdataTb4b4b4:
Tff7b72break
Tff7b72except Tf85149TimeoutErrorTb4b4b4:
Tff7b72pass

Te6edf3stored Tff7b72= Te6edf3mock_appTff7b72.Td2a8ffdatabaseTff7b72.Td2a8ffmiscTff7b72.Td2a8ffget_keyboard_shortcutsTb4b4b4(
Te6edf3mock_appTff7b72.Td2a8ffidentityTff7b72.Td2a8ffhashTff7b72.Td2a8ffhexTb4b4b4(Tb4b4b4)Tb4b4b4,
Tb4b4b4)
Tff7b72assert Tffa657allTb4b4b4(Te6edf3sTb4b4b4[Ta5d6ff"Ta5d6ffactionTa5d6ff"Tb4b4b4] Tff7b72!= Te6edf3probe_action Tff7b72for Te6edf3s Tff7b72in Te6edf3storedTb4b4b4)


Tff7b72def Td2a8ff_make_filesync_handlerTb4b4b4(Te6edf3tmp_pathTb4b4b4)Tb4b4b4:
Te6edf3storage Tff7b72= Te6edf3tmp_path Tff7b72/ Ta5d6ff"Ta5d6ffidentity-storageTa5d6ff"
Te6edf3storageTff7b72.Td2a8ffmkdirTb4b4b4(Tb4b4b4)
Tff7b72return Te6edf3RnsFilesyncHandlerTb4b4b4(
Te6edf3reticulum_instanceTff7b72=Te6edf3MagicMockTb4b4b4(Tb4b4b4)Tb4b4b4,
Te6edf3identityTff7b72=Te6edf3MagicMockTb4b4b4(Tb4b4b4)Tb4b4b4,
Te6edf3storage_dirTff7b72=Tffa657strTb4b4b4(Te6edf3storageTb4b4b4)Tb4b4b4,
Tb4b4b4)Tb4b4b4, Te6edf3storage


Tff7b72def Td2a8fftest_filesync_sync_dir_rejects_identity_ssl_dirTb4b4b4(Te6edf3tmp_pathTb4b4b4)Tb4b4b4:
T8b949e"""Invariant 3: ssl holds the identity TLS key and must not be syncable."""
Te6edf3handlerTb4b4b4, Te6edf3storage Tff7b72= Te6edf3_make_filesync_handlerTb4b4b4(Te6edf3tmp_pathTb4b4b4)
Te6edf3target Tff7b72= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffjoinTb4b4b4(Tffa657strTb4b4b4(Te6edf3storageTb4b4b4)Tb4b4b4, Ta5d6ff"Ta5d6ffsslTa5d6ff"Tb4b4b4)
Te6edf3osTff7b72.Td2a8ffmakedirsTb4b4b4(Te6edf3targetTb4b4b4, Te6edf3exist_okTff7b72=Tff7b72TrueTb4b4b4)
Tff7b72assert Te6edf3handlerTff7b72.Td2a8ff_resolve_sync_directoryTb4b4b4(Te6edf3targetTb4b4b4) Tff7b72is Tff7b72None


Tff7b72def Td2a8fftest_filesync_sync_dir_rejects_identity_root_and_escapeTb4b4b4(Te6edf3tmp_pathTb4b4b4)Tb4b4b4:
T8b949e"""Controls: identity root and sibling escape stay rejected today."""
Te6edf3handlerTb4b4b4, Te6edf3storage Tff7b72= Te6edf3_make_filesync_handlerTb4b4b4(Te6edf3tmp_pathTb4b4b4)
Tff7b72assert Te6edf3handlerTff7b72.Td2a8ff_resolve_sync_directoryTb4b4b4(Tffa657strTb4b4b4(Te6edf3storageTb4b4b4)Tb4b4b4) Tff7b72is Tff7b72None
Te6edf3sibling Tff7b72= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffjoinTb4b4b4(Tffa657strTb4b4b4(Te6edf3storageTb4b4b4)Tb4b4b4, Ta5d6ff"Ta5d6ff..Ta5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffother-identityTa5d6ff"Tb4b4b4)
Tff7b72assert Te6edf3handlerTff7b72.Td2a8ff_resolve_sync_directoryTb4b4b4(Te6edf3siblingTb4b4b4) Tff7b72is Tff7b72None


Tff7b72def Td2a8fftest_filesync_sync_dir_allows_plain_subdirectoryTb4b4b4(Te6edf3tmp_pathTb4b4b4)Tb4b4b4:
T8b949e"""Control: an ordinary identity-storage subdirectory stays selectable."""
Te6edf3handlerTb4b4b4, Te6edf3storage Tff7b72= Te6edf3_make_filesync_handlerTb4b4b4(Te6edf3tmp_pathTb4b4b4)
Te6edf3target Tff7b72= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffjoinTb4b4b4(Tffa657strTb4b4b4(Te6edf3storageTb4b4b4)Tb4b4b4, Ta5d6ff"Ta5d6ffshared-docsTa5d6ff"Tb4b4b4)
Te6edf3osTff7b72.Td2a8ffmakedirsTb4b4b4(Te6edf3targetTb4b4b4, Te6edf3exist_okTff7b72=Tff7b72TrueTb4b4b4)
Tff7b72assert Te6edf3handlerTff7b72.Td2a8ff_resolve_sync_directoryTb4b4b4(Te6edf3targetTb4b4b4) Tff7b72is Tff7b72not Tff7b72None


──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────